#include <iostream>
#include <exception>
#include "configuration.h"
#include <chrono>

using std::cerr;
Parameters Configuration::ParserParams(const string& config_file)
{
	Parameters Params;
	jsmn::Object params_json = jsmn::parse(config_file.c_str());

	//parse video params
	{
		jsmn::Object video_params = params_json["video settings"].unwrap<jsmn::Object>();
		
		if (!video_params.find("mode") || video_params["mode"].unwrap<jsmn::String>() == "online")
		{
			Params.video_params.video_mode = "online";
			Params.video_params.m_fps = static_cast<int>(video_params["mfps"].unwrap<jsmn::Number>());
			Params.video_params.video_source = video_params["video source"].unwrap<jsmn::String>();
		}
		else
		{
			Params.video_params.video_mode = "local";
			Params.video_params.video_source = video_params["video source"].unwrap<jsmn::String>();
			if (video_params.find("start frame"))
				Params.video_params.m_startFrame = static_cast<int>(video_params["start frame"].unwrap<jsmn::Number>());
			if (video_params.find("end frame"))
				Params.video_params.m_endFrame = static_cast<int>(video_params["end frame"].unwrap<jsmn::Number>());
			if (video_params.find("start time"))
			{
				std::string start_time = video_params["start time"].unwrap<jsmn::String>();
				std::tm t = {};
				std::istringstream ss(start_time);
				ss.imbue(std::locale::locale());
				ss >> std::get_time(&t, "%Y-%m-%d %H:%M:%S");
				time_t timetThen = mktime(&t);
				std::chrono::time_point<std::chrono::system_clock> then_tp = std::chrono::system_clock::from_time_t(timetThen);
				std::cout << "start time: " << std::put_time(&t, "%c") << std::endl;
				Params.video_params.start_time = std::chrono::time_point_cast<std::chrono::seconds>(then_tp);
			}
		}

		if (video_params.find("rotate"))
			Params.video_params.rotate = static_cast<int>(video_params["rotate"].unwrap<jsmn::Number>());
	}
	
	{
		Params.detector_mode = static_cast<int>(params_json["detector mode"].unwrap<jsmn::Number>());
	}

	if (Params.detector_mode == 0)
	{
		//parse detector params
		{
			jsmn::Object detector_params = params_json["openpose model"].unwrap<jsmn::Object>();

			jsmn::Array pose_net = detector_params["model"].unwrap<jsmn::Array>();
			Params.human_detector_params.model_path = pose_net[0].unwrap<jsmn::String>();
			Params.human_detector_params.deploy_path = pose_net[1].unwrap<jsmn::String>();

			Params.human_detector_params.width = static_cast<int>(detector_params["size"].unwrap<jsmn::Number>());
		}
	}
	else if (Params.detector_mode == 1)
	{
		//since openpose with kcf has bug, temporary use mxnet model
		{
			jsmn::Object detector_params = params_json["person detector"].unwrap<jsmn::Object>();

			Params.person_detector_params.model_path = detector_params["model"].unwrap<jsmn::String>();
			Params.person_detector_params.epoch = static_cast<int>(detector_params["epoch"].unwrap<jsmn::Number>());
			Params.person_detector_params.width = static_cast<int>(detector_params["width"].unwrap<jsmn::Number>());
			Params.person_detector_params.height = static_cast<int>(detector_params["height"].unwrap<jsmn::Number>());

			Params.person_detector_params.mean_r = detector_params["mean_r"].unwrap<jsmn::Number>();
			Params.person_detector_params.mean_g = detector_params["mean_g"].unwrap<jsmn::Number>();
			Params.person_detector_params.mean_b = detector_params["mean_b"].unwrap<jsmn::Number>();

			Params.person_detector_params.device_type = detector_params["device type"].unwrap<jsmn::String>() == "GPU" ? 2 : 1;
			Params.person_detector_params.device_id = static_cast<int>(detector_params["device id"].unwrap<jsmn::Number>());

			Params.person_detector_params.thresh = detector_params["threshold"].unwrap<jsmn::Number>();
		}
	}
	
	//parse face detector
	{
		jsmn::Object face_det_params = params_json["face detector"].unwrap<jsmn::Object>();

		jsmn::Array mtcnn_net = face_det_params["model"].unwrap<jsmn::Array>();
		Params.mtcnn_params.p_net = mtcnn_net[0].unwrap<jsmn::String>();
		Params.mtcnn_params.r_net = mtcnn_net[1].unwrap<jsmn::String>();
		Params.mtcnn_params.o_net = mtcnn_net[2].unwrap<jsmn::String>();

		Params.mtcnn_params.width = static_cast<int>(face_det_params["width"].unwrap<jsmn::Number>());
		Params.mtcnn_params.height = static_cast<int>(face_det_params["height"].unwrap<jsmn::Number>());
	}

	//parse face info
	{
		jsmn::Object detector_params = params_json["face info"].unwrap<jsmn::Object>();

		jsmn::Array models = detector_params["model"].unwrap<jsmn::Array>();
		for (auto model:models)
		{
			Params.face_info_params.models.emplace_back(model.unwrap<jsmn::String>());
		}

		Params.face_info_params.epoch = static_cast<int>(detector_params["epoch"].unwrap<jsmn::Number>());
		Params.face_info_params.width = static_cast<int>(detector_params["width"].unwrap<jsmn::Number>());
		Params.face_info_params.height = static_cast<int>(detector_params["height"].unwrap<jsmn::Number>());

		Params.face_info_params.mean_r = detector_params["mean_r"].unwrap<jsmn::Number>();
		Params.face_info_params.mean_g = detector_params["mean_g"].unwrap<jsmn::Number>();
		Params.face_info_params.mean_b = detector_params["mean_b"].unwrap<jsmn::Number>();

		Params.face_info_params.device_type = detector_params["device type"].unwrap<jsmn::String>() == "GPU" ? 2 : 1;
		Params.face_info_params.device_id = static_cast<int>(detector_params["device id"].unwrap<jsmn::Number>());

		Params.face_info_params.thresh = detector_params["threshold"].unwrap<jsmn::Number>();
	}

	//parse face id
	{
		jsmn::Object face_id = params_json["face id"].unwrap<jsmn::Object>();

		if (face_id["enable"].unwrap<jsmn::Boolean>())
		{
			Params.face_ID_params.enable = true;
			jsmn::Array face_id_net = face_id["model"].unwrap<jsmn::Array>();
			Params.face_ID_params.model_path = face_id_net[0].unwrap<jsmn::String>();
			Params.face_ID_params.deploy_path = face_id_net[1].unwrap<jsmn::String>();
			Params.face_ID_params.feature_mean = face_id["feature mean"].unwrap<jsmn::String>();
			Params.face_ID_params.thresh = face_id["threshold"].unwrap<jsmn::Number>();
		}
		else
		{
			Params.face_ID_params.enable = false;
		}
	}

	//parse door settings
	{
		jsmn::Array door_setting = params_json["door settings"].unwrap<jsmn::Array>();

		Params.door_params.door_left_x = static_cast<int>(door_setting[0].unwrap<jsmn::Number>());
		Params.door_params.door_left_y = static_cast<int>(door_setting[1].unwrap<jsmn::Number>());
		Params.door_params.door_right_x = static_cast<int>(door_setting[2].unwrap<jsmn::Number>());
		Params.door_params.door_right_y = static_cast<int>(door_setting[3].unwrap<jsmn::Number>());
	}

	//parse render settings
	{
		jsmn::Object render_setting = params_json["render"].unwrap<jsmn::Object>();

		Params.render_params.background_image = render_setting["background image"].unwrap<jsmn::String>();
		Params.render_params.person_image = render_setting["person image"].unwrap<jsmn::String>();

		if (render_setting.find("draw rect"))
		{
			Params.render_params.draw_rect = render_setting["draw rect"].unwrap<jsmn::Boolean>();
		}

		if (render_setting.find("mode"))
		{
			Params.render_params.render_mode = static_cast<int>(render_setting["mode"].unwrap<jsmn::Number>());
		}
	}

	//parse databse
	{
		jsmn::Object database_param = params_json["database"].unwrap<jsmn::Object>();

		Params.database_params.database_folder = database_param["image folder"].unwrap<jsmn::String>();
		Params.database_params.databse_record = database_param["feature record"].unwrap<jsmn::String>();
	}

	//parse track settings
	{
		if (params_json.find("tracker settings"))
		{
			jsmn::Object tracker_set = params_json["tracker settings"].unwrap<jsmn::Object>();

			Params.track_params.max_track_length = static_cast<int>(tracker_set["max_track_length"].unwrap<jsmn::Number>());
		}
	}

	{
		if (params_json.find("save face"))
		{
			jsmn::Object save_params = params_json["save face"].unwrap<jsmn::Object>();
			Params.issave = save_params["issave"].unwrap<jsmn::Boolean>();
			Params.save_face_path = save_params["save path"].unwrap<jsmn::String>();
		}
	}

	//parse statistics
	{
		if (params_json.find("statistics"))
		{
			jsmn::Object statistic_set = params_json["statistics"].unwrap<jsmn::Object>();

			if (statistic_set.find("init num_in"))
			{
				Params.statistic_params.num_in = static_cast<int>(statistic_set["init num_in"].unwrap<jsmn::Number>());
			}

			if (statistic_set.find("init num_out"))
			{
				Params.statistic_params.num_out = static_cast<int>(statistic_set["init num_out"].unwrap<jsmn::Number>());
			}

			if (statistic_set.find("init vip_in"))
			{
				Params.statistic_params.vip_in = static_cast<int>(statistic_set["init vip_in"].unwrap<jsmn::Number>());
			}

			if (statistic_set.find("init non_vip_in"))
			{
				Params.statistic_params.non_vip_in = static_cast<int>(statistic_set["init non_vip_in"].unwrap<jsmn::Number>());
			}

			if (statistic_set.find("init female_in"))
			{
				Params.statistic_params.female_in = static_cast<int>(statistic_set["init female_in"].unwrap<jsmn::Number>());
			}

			if (statistic_set.find("init male_in"))
			{
				Params.statistic_params.male_in = static_cast<int>(statistic_set["init male_in"].unwrap<jsmn::Number>());
			}
		}
	}
	return Params;
}